package com.example.gbuffer;

import android.content.Context;
import android.opengl.GLES32;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;


import com.example.gbuffer.Component.Camera.PerspectiveCamera;
import com.example.gbuffer.Component.Framebuffer.GeoFramebuffer;
import com.example.gbuffer.Component.Framebuffer.ScreenFramebuffer;
import com.example.gbuffer.Component.Framebuffer.SurfaceFramebuffer;
import com.example.gbuffer.Component.Light.PointLight;
import com.example.gbuffer.RenderPass.GeoRenderPass;
import com.example.gbuffer.RenderPass.LightCubeRenderPass;
import com.example.gbuffer.RenderPass.LightingRenderPass;
import com.example.gbuffer.RenderPass.ScreenRenderPass;
import com.example.gbuffer.RenderPass.VisualizeGBufferRenderPass;
import com.example.gbuffer.Utils.GLViewportRect;
import com.example.gbuffer.Utils.OpenGLUtils;

import java.nio.IntBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;

public class GLRender implements Renderer {

    private final String LOG_TAG = "GLRender";
    private Context m_context;
    private PerspectiveCamera m_camera_p = new PerspectiveCamera();
    private GeoRenderPass m_geo_render_pass;
    private VisualizeGBufferRenderPass m_visual_buffer_pass;
    private LightingRenderPass m_lighting_pass;
    private LightCubeRenderPass m_light_cube_pass;
    private ScreenRenderPass m_screen_pass;

    private final PointLight[] m_point_light_list = new PointLight[2];

    private SurfaceFramebuffer m_surface_buffer = new SurfaceFramebuffer();
    private GeoFramebuffer m_geo_buffer;
    private ScreenFramebuffer m_screen_buffer;

    private int m_width = 1920;
    private int m_height = 1080;

    private IntBuffer m_test_tex_id;

    private boolean m_visualize_switch = false;


    public GLRender(Context context)
    {
        m_context = context;
    }

    @Override
    public void onDrawFrame(GL10 gl)
    {
        // Log.d(LOG_TAG, "--------onDrawFrame-------");
        drawFrame();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        Log.i(LOG_TAG, "onSurfaceChanged:: w: " + width + " h: " + height);
        this.m_width = width;
        this.m_height = height;

        initResources();

        GLES32.glViewport(0,0,width,height);
        m_camera_p.setAspect((float)width/height);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {

        printGlInformation();

        GLES32.glClearColor(0.2F, 0.2F, 0.2F, 1.0F);      // 不要不写小数点！否则会没用。
        GLES32.glEnable(GLES32.GL_DEPTH_TEST);
    }

    /**
     * @brief 初始化opengl es所需资源
     */
    private void initResources()
    {

        // 设置摄像机
        m_camera_p.setFovy(65.0f);
        m_camera_p.setAspect((float) m_width / m_height);
        m_camera_p.setZNear(0.1f);
        m_camera_p.setZFar(100.0f);

        // 设置光源
        m_point_light_list[0] = new PointLight();
        m_point_light_list[1] = new PointLight();
        m_point_light_list[0].setPosition(0.0f, 0.0f, -6.0f);
        m_point_light_list[1].setPosition(-2.2f, 1.3f, 6.2f);
//        m_point_light_list[1].setLightColor(0.5f, 0.0f, 0.0f);

        m_screen_buffer = new ScreenFramebuffer(m_width, m_height);
        m_geo_buffer = new GeoFramebuffer(m_width, m_height);
        m_geo_render_pass = new GeoRenderPass(m_geo_buffer);
        m_geo_render_pass.setViewport(new GLViewportRect(0,0, m_width, m_height));
        m_geo_render_pass.initResources(m_context);     // context is ready when surface is created.
        m_geo_render_pass.setPerspectiveCamera(m_camera_p);

        m_visual_buffer_pass = new VisualizeGBufferRenderPass(m_surface_buffer);
        m_visual_buffer_pass.initResources(m_context);

        m_lighting_pass = new LightingRenderPass(m_screen_buffer);
        m_lighting_pass.initResources(m_context);
        m_lighting_pass.setPerspectiveCamera(m_camera_p);
        m_lighting_pass.setPointLights(m_point_light_list);

        m_light_cube_pass = new LightCubeRenderPass(m_screen_buffer);
        m_light_cube_pass.setViewport(new GLViewportRect(0,0, m_width, m_height));
        m_light_cube_pass.initResources(m_context);
        m_light_cube_pass.setPerspectiveCamera(m_camera_p);
        m_light_cube_pass.setLights(m_point_light_list);

        m_screen_pass = new ScreenRenderPass(m_surface_buffer);
        m_screen_pass.setViewport(new GLViewportRect(0,0, m_width, m_height));
        m_screen_pass.initResources(m_context);
    }

    /**
     * @brief 绘制一帧
     */
    private void drawFrame()
    {
        m_geo_render_pass.executeDrawCalls();

        lightingGBuffer();

        GLES32.glClear(GLES32.GL_DEPTH_BUFFER_BIT);
        OpenGLUtils.copyDepthBufferBetweenFramebuffer(m_geo_buffer.fbId(), new GLViewportRect(0,0,m_width,m_height),
                m_screen_buffer.fbId(), new GLViewportRect(0, 0, m_width, m_height));

        m_light_cube_pass.executeDrawCalls();

        ScreenRenderPass.setScreenTextureId(m_screen_buffer.getScreenTextureId());
        m_screen_pass.executeDrawCalls();
    }

    private void printGlInformation() {
        Log.i(LOG_TAG, "opengl version: " + GLES32.glGetString(GL10.GL_VERSION));

        // 计算最大整数数组长度
        int maxIntArrayLength = OpenGLUtils.checkMaxFragmentUniformVectors();
        Log.i(LOG_TAG, "max fragment uniform vectors: " + maxIntArrayLength);
    }

    /**
     * @brief 将GBuffer里的内容显示到屏幕上
     */
    private void visualizeGBuffer() {
        //        int testTextureId = OpenGLUtils.generateTextureRGBA(m_width, m_height, 1.0f, 0.0f, 0.0f, 1.0f);
        //        VisualizeGBufferRenderPass.setGeoTexture(testTextureId);

        VisualizeGBufferRenderPass.setGeoTextureId(m_geo_buffer.getBufferTextureId(GeoFramebuffer.BufferType.POSITION_BUFFER));
        VisualizeGBufferRenderPass.setGeoTextureType(0);
        m_visual_buffer_pass.setViewport(new GLViewportRect(0,0, m_width/2, m_height/2));    // bottom-left;
        m_visual_buffer_pass.executeDrawCalls();

        VisualizeGBufferRenderPass.setGeoTextureId(m_geo_buffer.getBufferTextureId(GeoFramebuffer.BufferType.NORMAL_BUFFER));
        VisualizeGBufferRenderPass.setGeoTextureType(1);
        m_visual_buffer_pass.setViewport(new GLViewportRect(0, m_height/2, m_width/2, m_height/2));    // top-left;
        m_visual_buffer_pass.executeDrawCalls();

        VisualizeGBufferRenderPass.setGeoTextureId(m_geo_buffer.getBufferTextureId(GeoFramebuffer.BufferType.COLOR_SPEC_BUFFER));
        VisualizeGBufferRenderPass.setGeoTextureType(2);
        m_visual_buffer_pass.setViewport(new GLViewportRect(m_width/2, m_height/2, m_width/2, m_height/2));    // top-right;
        m_visual_buffer_pass.executeDrawCalls();

        VisualizeGBufferRenderPass.setGeoTextureId(m_geo_buffer.getBufferTextureId(GeoFramebuffer.BufferType.COLOR_SPEC_BUFFER));
        VisualizeGBufferRenderPass.setGeoTextureType(3);
        m_visual_buffer_pass.setViewport(new GLViewportRect(m_width/2, 0, m_width/2, m_height/2));    // top-right;
        m_visual_buffer_pass.executeDrawCalls();
    }

    private void lightingGBuffer() {

        LightingRenderPass.setPositionTextureId( m_geo_buffer.getBufferTextureId(GeoFramebuffer.BufferType.POSITION_BUFFER));
        LightingRenderPass.setNormalTextureId( m_geo_buffer.getBufferTextureId(GeoFramebuffer.BufferType.NORMAL_BUFFER));
        LightingRenderPass.setAlbedoSpecTextureId( m_geo_buffer.getBufferTextureId(GeoFramebuffer.BufferType.COLOR_SPEC_BUFFER));

        m_lighting_pass.setViewport(new GLViewportRect(0, 0, m_width, m_height));
        m_lighting_pass.executeDrawCalls();
    }
}